# coding=utf-8
import datetime
import decimal
import os
import sys

import click
# 从flask包导入Flask类
from flask import Flask, render_template, url_for, request, flash, redirect, jsonify, make_response
from flask_login import LoginManager, login_user, logout_user, login_required, current_user, UserMixin
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy  # 导入扩展类
from werkzeug.security import generate_password_hash, check_password_hash

# from markupsafe import escape

# 创建一个Flask类的对象，注意参数__name__是系统变量，是指本py文件的文件名
# 让flask.helpers.get_root_path函数通过传入这个名字确定程序的根目录，以便获得静态文件和模板文件的目录。
app = Flask(__name__)
# app.config['secret_key'] = 'dev'  # os.urandom(24)  # 等同于 app.secret_key = XXXX
app.secret_key = 'development key'
# app.config.JSONIFY_MIMETYPE = 'application/json;charset=utf-8'

login_manager = LoginManager(app)
login_manager.login_view = 'login'
login_manager.login_message = ''
# app.login_required = login_required

# 数据库创建
WIN = sys.platform.startswith('win')
if WIN:
    prefix = 'sqlite:///'
else:
    prefix = 'sqlite:////'

app.config['SQLALCHEMY_DATABASE_URI'] = prefix + os.path.join(app.root_path, 'data.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False  # 关闭对模型修改的监控
db = SQLAlchemy(app)  # 初始化扩展,传入程序实例.
migrate = Migrate(app, db)


# 客户端发送url给web服务器，web服务器将url转发给flask程序实例
# 程序实例需要知道对于每一个url请求启动哪一部分代码
# 使用app.route装饰器会将URL和执行的视图函数的关系保存到app.url_map属性上。
# @app.route('/index')
# @app.route('/home')
# def hello_world():  # put application's code here
#     return '<h1>Hello World!</h1><img src="http://helloflask.com/totoro.gif">'
#
#
# @app.route("/user/<name>")
# def user_page(name):
#     return f"user:{escape(name)}"
#
#
# @app.route('/test')
# def test_url_for():
#     print(url_for('hello_world'))
#     print(url_for('user_page', name='John'))
#     print(url_for('test_url_for', num=2))
#     return 'Test page'

@app.route("/", methods=['GET', 'POST'])
@login_required
def index():
    # sps = StickyPaper.query.filter_by(organization_id=current_user.organization_id).all()
    sps = Organization.query.filter_by(id=current_user.organization_id).first().sticky_papers
    payment_modes = PaymentMode.query.all()
    accounting_tiles = AccountingTitle.query.all()
    # print(accounting_tiles[1].accounting_title)
    # print(type(payment_modes))
    # print(sps[0].edit_time.strftime("%Y-%m-%d %H:%M:%S"))
    return render_template('index.html',
                           sps=sps,
                           payment_modes=payment_modes,
                           accounting_tiles=accounting_tiles)


@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))


@app.route("/login_X", methods=['POST'])
def login_X():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        user = User.query.filter(User.username == username).first()
        # print(user.name)
        if user:

            if user.validate_password(password):
                response = make_response(user.name + "[OK]")
                response.headers['Content-Type'] = 'application/text; charset=utf-8'
                login_user(user)  # 登入用户，由flask_login 模块提供。
                # load_user(user.id)  # 加载用户
                return response
            else:
                response = make_response('密码不正确！')
                response.headers['Content-Type'] = 'application/text; charset=utf-8'
                return response
        else:
            response = make_response("用户不存在！")
            response.headers['Content-Type'] = 'application/text; charset=utf-8'
            return response


@app.context_processor
def inject_organization():
    if current_user.is_authenticated:
        # print(Organization.query.get(1))
        # print(current_user.organization_id)
        return {'organization': Organization.query.get(current_user.organization_id)}
    else:
        return {'organization': ''}


@app.route('/login', methods=['GET', 'POST'])
def login():
    # if request.method == 'POST':
    #     username = request.form.get('username')
    #     password = request.form.get('password')
    #
    #     if not username or not password:
    #         flash('请输入用户名和密码')
    #         return redirect(url_for('login'))
    #
    #     user = User.query.filter_by(username='admin').first()
    #     if username == user.username and user.validate_password(password):
    #         login_user(user)
    #         flash('管理员已登陆')
    #         return redirect(url_for('index'))
    #     flash('用户名或者密码错误')
    #     return redirect(url_for('login'))

    return render_template('login.html')


# login_manager.login_view = 'login'


@app.route('/logout')
def logout():
    logout_user()
    # flash('用户已正常退出')
    return redirect(url_for('login'))


# 模板上下文处理
@app.context_processor
def inject_user():
    # user = User.query.first()
    return {'user': current_user}


@app.route("/add_sp", methods=['POST'])
@login_required
def add_sp():
    if request.method == 'POST':
        # print(request.form)
        # 把提交的数据存储到数据库中
        # 摘要
        summary = request.form.get('summary')

        # 单据张数
        paper_count = request.form.get('paper_count')

        # 支付方式
        payment_mode = request.form.get('payment_mode')
        # assert isinstance(PaymentMode.query.filter_by(payment_mode=payment_mode).first().id, int)
        payment_mode_id = int(PaymentMode.query.filter_by(payment_mode=payment_mode).first().id)  # 从表中取出id
        # 会计科目
        accounting_title = request.form.get('accounting_title')
        accounting_title_id = int(AccountingTitle.query.filter_by(accounting_title=accounting_title).first().id)
        # 合计金额
        # assert isinstance(amount, decimal.Decimal)
        amount = decimal.Decimal(request.form.get('amount'))

        # 用户
        user_id = int(current_user.id)

        # 所属学校
        organization_id = current_user.organization_id

        # 单据状态
        doc_status_id = 1

        # 编辑时间
        edit_time = datetime.datetime.now()

        spx = StickyPaper(summary=summary,
                          paper_count=paper_count,
                          accounting_title_id=accounting_title_id,
                          payment_mode_id=payment_mode_id,
                          amount=amount,
                          user_id=user_id,
                          organization_id=organization_id,
                          doc_status_id=doc_status_id,
                          edit_time=edit_time)
        db.session.add(spx)
        db.session.commit()
        # flash('Your sp has been added.', 'success')

        # response = make_response(edit_time.strftime('%Y-%m-%d %H:%M:%S'))
        # response.headers['Content-Type'] = 'application/text; charset=utf-8'
        return jsonify({'edit_time': edit_time.strftime('%Y-%m-%d %H:%M:%S'), 'sp_id': spx.id})


@app.route('/movie/delete/<int:sticky_paper_id>', methods=['GET', 'Post'])
@login_required
def delete(sticky_paper_id):
    sticky_paper = StickyPaper.query.get_or_404(sticky_paper_id)
    db.session.delete(sticky_paper)
    db.session.commit()
    flash('删除成功！', 'success')
    return redirect(url_for('index'))


# 更新条目
# @app.route('/movie/update/<int:sticky_paper_id>', methods=['GET', 'Post'])
@app.route('/update', methods=['GET', 'Post'])
@login_required
def update():
    if request.method == 'POST':
        # print(request.form)
        # 把提交的数据存储到数据库中
        # 取得粘贴单ID
        id = request.form.get("sp_id")
        sticky_paper = StickyPaper.query.get_or_404(id)
        if sticky_paper:
            # 摘要
            sticky_paper.summary = request.form.get('summary_update')

            # 单据张数
            sticky_paper.paper_count = request.form.get('paper_count_update')

            # 支付方式
            payment_mode = request.form.get('payment_mode_update')
            # assert isinstance(PaymentMode.query.filter_by(payment_mode=payment_mode).first().id, int)
            sticky_paper.payment_mode_id = int(
                PaymentMode.query.filter_by(payment_mode=payment_mode).first().id)  # 从表中取出id
            # 会计科目
            accounting_title = request.form.get('accounting_title_update')
            sticky_paper.accounting_title_id = int(
                AccountingTitle.query.filter_by(accounting_title=accounting_title).first().id)
            # 合计金额
            # assert isinstance(amount, decimal.Decimal)
            sticky_paper.amount = decimal.Decimal(request.form.get('amount_update'))

            # 用户
            sticky_paper.user_id = int(current_user.id)

            # 所属学校
            sticky_paper.organization_id = current_user.organization_id

            # 单据状态
            sticky_paper.doc_status_id = 1

            # 编辑时间
            sticky_paper.edit_time = datetime.datetime.now()

            db.session.commit()
            # flash('Your sp has been added.', 'success')

            response = make_response(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
            response.headers['Content-Type'] = 'application/text; charset=utf-8'
            return response
    # if request.method == 'POST':
    #     title = request.form.get('title', type=str)
    #     year = request.form.get('year')
    #     if not title or not year or len(year) != 4 or len(title) > 60:
    #         flash('条目无效.', 'danger')
    #         return redirect(url_for('edit', movie_id=sticky_paper_id))
    #
    #     sticky_paper.title = title
    #     sticky_paper.year = year
    #     db.session.commit()
    #     flash(title + '已经更新', 'success')
    #     return redirect(url_for('index'))
    # else:
    #     sticky_papers = Movie.query.all()
    #     return render_template('edit.html', movie=sticky_paper, sticky_paper=sticky_paper)


#  __name__ == '__main__'是python常用的方法，表示只有直接启动【本】脚本时候，才用app.run方法
#  如果是其他脚本调用本脚本，程序假定父级脚本会启用不同的服务器，因此不用执行app.run()
# app.run(host = "127.0.0.1", port = 9000)
# 执行app.run就可以启动服务了。默认Flask只监听虚拟机的本地127.0.0.1这个地址，端口为5000。
# 而我们对虚拟机做的端口转发端口是9000，所以需要制定host和port参数，
# 0.0.0.0表示监听所有地址，这样就可以在本机访问了。
# 服务器启动后，会调用werkzeug.serving.run_simple进入轮询，
# 默认使用单进程单线程的werkzeug.serving.BaseWSGIServer处理请求，
# 实际上还是使用标准库BaseHTTPServer.HTTPServer，通过select.select做0.5秒的“while TRUE”的事件轮询。
# 当我们访问“http: // 127.0.0.1: 9000 /”,
# 通过app.url_map找到注册的“ / ”这个URL模式, 就找到了对应的hello_world函数执行，
# 返回“helloworld!”, 状态码为200。
#  如果访问一个不存在的路径，如访问“http: // 127.0.0.1: 9000 / a”,
#  Flask找不到对应的模式，就会向浏览器返回“NotFound”，状态码为404.

# V ---------------------------------------------------------------------------------------------------------------
# 用户表
class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)  # 主键
    name = db.Column(db.String(80))  # 昵称
    username = db.Column(db.String(80), unique=True)  # 用户名
    organization_id = db.Column(db.Integer, db.ForeignKey('organization.id'))
    organization = db.relationship('Organization', backref=db.backref('users'))

    password_hash = db.Column(db.String(128))  # 密码散列值

    def __init__(self, name, username):
        self.name = name
        self.username = username

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def validate_password(self, password):
        return check_password_hash(self.password_hash, password)


# 单位信息表
class Organization(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), unique=True)  # 单位名称
    abbreviation = db.Column(db.String(80), unique=True)  # 缩写


class Movie(db.Model):  # 表名将会是 movie,自动生成,小写处理.
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(60))  # 电影标题
    year = db.Column(db.String(4))  # 电影年份


# 粘贴单
class StickyPaper(db.Model):  # 表名将会是 sticky_paper,自动生成,小写处理.
    id = db.Column(db.Integer, primary_key=True)
    summary = db.Column(db.String(200))  # 摘要
    paper_count = db.Column(db.Integer)  # 张数

    # 会计科目
    accounting_title_id = db.Column(db.Integer, db.ForeignKey('accounting_title.id'))
    accounting_title = db.relationship('AccountingTitle', backref=db.backref('sticky_papers'))
    # 报销方式
    payment_mode_id = db.Column(db.Integer, db.ForeignKey('payment_mode.id'))
    payment_mode = db.relationship('PaymentMode', backref=db.backref('sticky_papers'))

    amount = db.Column(db.Numeric(15, 2))  # 合计金额
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    user = db.relationship('User', backref=db.backref('sticky_papers'))

    organization_id = db.Column(db.Integer, db.ForeignKey('organization.id'))
    organization = db.relationship('Organization', backref=db.backref('sticky_papers'))

    doc_status_id = db.Column(db.Integer, db.ForeignKey('doc_status.id'))
    doc_status = db.relationship('DocStatus', backref=db.backref('sticky_papers'))

    edit_time = db.Column(db.DateTime, default=datetime.datetime.now())
    # accountant = db.column(db.String(20))  # 会计
    # organization = db.Column(db.String(100))  # 所在单位
    # master_Doc_type = db.Column(db.String(200))  # 主单类型


# 支付方式
class PaymentMode(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    payment_mode = db.Column(db.String(50), unique=True, nullable=False)
    note = db.Column(db.String(200))


# 会计科目
class AccountingTitle(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    accounting_title = db.Column(db.String(80), unique=True, nullable=False)
    note = db.Column(db.String(200))


# 主单信息
class MasterDoc(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    # 单据类型
    master_doc_type_id = db.Column(db.Integer, db.ForeignKey('master_doc_type.id'))
    master_doc_type = db.relationship('MasterDocType', backref=db.backref('master_docs', lazy=True))

    # 销售方或者参与人
    seller_or_participant_id = db.Column(db.Integer, db.ForeignKey('seller_or_participant.id'))
    seller_or_participant = db.relationship('SellerOrParticipant', backref=db.backref('master_docs', lazy=True))

    paper_count = db.Column(db.Integer(), nullable=False)  # 单据张数
    number = db.Column(db.String(64), nullable=False)  # 单据号码
    issue_date = db.Column(db.Date, nullable=False)  # 开具日期
    amount = db.Column(db.Numeric, nullable=False)  # 票面金额
    reimbursement_amount = db.Column(db.Numeric, nullable=False)  # 报销金额

    # 领款人
    payee_id = db.Column(db.Integer, db.ForeignKey('payee.id'))
    payee = db.relationship('Payee', backref=db.backref('master_docs', lazy=True))
    notes = db.Column(db.String(200))


# 主单类型
class MasterDocType(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    doc_type = db.Column(db.String(100), unique=True, nullable=False)
    notes = db.Column(db.String(200))


# 销售方或参与人
class SellerOrParticipant(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    note = db.Column(db.String(200))


# 领款人
class Payee(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    bank_name = db.Column(db.String(100), nullable=False)
    bank_account_number = db.Column(db.String(100), unique=True, nullable=False)
    note = db.Column(db.String(200))


# 附件详细信息
class Attachment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    paper_count = db.Column(db.Integer, nullable=False)

    # 附件类型
    att_type_id = db.Column(db.Integer, db.ForeignKey('att_type.id'))
    att_type = db.relationship('AttType', backref=db.backref('attachments', lazy=True))
    amount = db.Column(db.Numeric)  # 附件金额
    payment_date = db.Column(db.Date)  # 支付日期


# 附件类型
class AttType(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    att_type = db.Column(db.String(100), unique=True, nullable=False)
    notes = db.Column(db.String(200))


# 单据状态
class DocStatus(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    doc_status = db.Column(db.String(100), unique=True, nullable=False)
    notes = db.Column(db.String(300))


# ^----------------------------------------------------------------------------------------------------------------

@app.cli.command()
@click.option('--drop', is_flag=True, help='Create after drop.')
def initdb(drop):
    """Initialize the database."""
    if drop:
        db.drop_all()
    db.create_all()
    click.echo('Initialized database.')


@app.cli.command()
@click.option('--username', prompt=True, help='The username used to login.')
@click.option('--password', prompt=True, hide_input=True, confirmation_prompt=True, help='The password used to login.')
def admin(username, password):
    """ create user. """
    # db.create_all()

    user = User.query.filter_by(username=username).first()
    if user is not None:
        click.echo('Updating user...')
        # user.name = 'zzw'
        user.username = username
        user.set_password(password)
    else:
        click.echo('Creating new user...')
        user = User(name='zzw', username=username)
        user.set_password(password)

    db.session.commit()
    click.echo('Done.')


@app.cli.command()
def forge():
    """Generate fake data for testing"""
    db.create_all()

    name = 'zzw'
    movies = [
        {'title': 'My Neighbor Totoro', 'year': '1988'},
        {'title': 'Dead Poets Society', 'year': '1989'},
        {'title': 'A Perfect World', 'year': '1993'},
        {'title': 'Leon', 'year': '1994'},
        {'title': 'Mahjong', 'year': '1996'},
        {'title': 'Swallowtail Butterfly', 'year': '1996'},
        {'title': 'King of Comedy', 'year': '1999'},
        {'title': 'Devils on the Doorstep', 'year': '1999'},
        {'title': 'WALL-E', 'year': '2008'},
        {'title': 'The Pork of Music', 'year': '2012'},
    ]
    user = User(name=name, username='admin')
    db.session.add(user)
    for m in movies:
        movie = Movie(title=m['title'], year=m['year'])
        db.session.add(movie)
    db.session.commit()
    click.echo('Generated fake data for testing')


@app.route('/testLocalStorage', methods=['GET', 'POST'])
def test_local_storage():
    return render_template('testLocalStorage.html')


@app.route('/testDOM', methods=['GET', 'POST'])
def test_dom():
    return render_template('testDOM.html')


@app.route('/testCSS', methods=['GET', 'POST'])
def test_css():
    return render_template('testCSS.html')


@app.route('/result', methods=['GET', 'POST'])
def result():
    data1 = {
        "test": "这是一个测试"
    }

    response = make_response(jsonify(data1))
    # response.headers['x-content-type-options'] = 'nosniff'
    response.headers['Content-Type'] = 'application/json;charset=utf-8'

    return response


@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404


@app.context_processor
def inject_date_show():
    date_show = datetime.date.today()
    return {'date_show': date_show}


if __name__ == '__main__':
    app.json.response()
    app.run()
